using System;
using System.Data;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using Microsoft.CommerceServer;
using Microsoft.CommerceServer.Catalog;
using Microsoft.CommerceServer.Catalog.Tools;

namespace MarkEmptyCategories
{
	public class MarkEmptyCategories
	{
		#region Private Fields
		/// <summary>
		/// The site Name.
		/// </summary>
		private string siteName = null;
		/// <summary>
		/// Collection of catalog names that needs its empty categories to be marked
		/// </summary>
		private StringCollection catalogNames = null;
		/// <summary>
		/// The boolean property mark true all the empty categories
		/// </summary>
		private string propertyName = null;
		/// <summary>
		/// Flag indicating whether inventory system should be ignored if present
		/// </summary>
		private bool ignoreInventorySystem = false;
		/// <summary>
		/// If <c>true</c> will filter products that are out of stock.
		/// </summary>
		private bool filterOutOfStockSkus = false;
		/// <summary>
		/// If <c>true</c> will filter products that can be back ordered.
		/// </summary>
		private bool filterBackorderableSkus = false;
		/// <summary>
		/// If <c>true</c> will filter products that can be pre ordered.
		/// </summary>
		private bool filterPreorderableSkus = false;
		#endregion

		#region Public Properties
		/// <summary>
		/// The site Name.
		/// </summary>
		public string SiteName
		{
			get { return siteName; }
		}

		/// <summary>
		/// Collection of catalog names that needs its empty categories to be marked
		/// </summary>
		public StringCollection CatalogNames
		{
			get { return catalogNames; }
		}

		/// <summary>
		/// The boolean property mark true all the empty categories
		/// </summary>
		public string PropertyName
		{
			get { return propertyName; }
		}

		/// <summary>
		/// Flag indicating whether inventory system should be ignored if present
		/// </summary>
		public bool IgnoreInventorySystem
		{
			get { return ignoreInventorySystem; }
		}

		/// <summary>
		/// If <c>true</c> will filter products that are out of stock.
		/// </summary>
		public bool FilterOutOfStockSkus
		{
			get { return filterOutOfStockSkus; }
		}

		/// <summary>
		/// If <c>true</c> will filter products that can be back ordered.
		/// </summary>
		public bool FilterBackorderableSkus
		{
			get { return filterBackorderableSkus; }
		}

		/// <summary>
		/// If <c>true</c> will filter products that can be pre ordered.
		/// </summary>
		public bool FilterPreorderableSkus
		{
			get { return filterPreorderableSkus; }
		}
		#endregion

		/// <summary>
		/// Public Constructor
		/// </summary>
		public MarkEmptyCategories()
		{
			catalogNames = new StringCollection();
		}
		/// <summary>
		/// Method to parse to the command line arguments to generate the required input data
		/// </summary>
		/// <param name="args">The array of command line arguments</param>
		public bool ParseArguments(string[] args)
		{
			StringCollection argumentCollection = new StringCollection();
			Regex regularExpression = new Regex(@"(^(/|-)[A-z]+:)(?=[A-z])");
			int index = 0;
			try
			{
				argumentCollection.AddRange(args);
				//Add proeprty parameters according to whethere these is a space between ":" and the value for the argument
				for (int i = 0, collectionIndex = 0; i < args.Length; i++, collectionIndex++)
				{
					if (regularExpression.IsMatch(args[i]))
					{
						index = args[i].IndexOf(":");
						argumentCollection[collectionIndex] = args[i].Substring(0, index + 1);
						collectionIndex++;
						argumentCollection.Insert(collectionIndex, args[i].Substring(index + 1));
					}
				}
				//Parsing the arguments
				for (int i = 0; i < argumentCollection.Count; i++)
				{
					switch (argumentCollection[i].ToUpper())
					{
						case "/S:":
						case "-S:":
							siteName = argumentCollection[++i];
							break;
						case "/C:":
						case "-C:":
							catalogNames.AddRange(argumentCollection[++i].Split(','));
							break;
						case "/P:":
						case "-P:":
							propertyName = argumentCollection[++i];
							break;
						case "/I:":
						case "-I:":
							ignoreInventorySystem = argumentCollection[++i].ToLower().Equals("true") ? true : false;
							break;
						case "/O:":
						case "-O:":
							filterOutOfStockSkus = argumentCollection[++i].ToLower().Equals("true") ? true : false;
							break;
						case "/BO:":
						case "-BO:":
							filterBackorderableSkus = argumentCollection[++i].ToLower().Equals("true") ? true : false;
							break;
						case "/PO:":
						case "-PO:":
							filterPreorderableSkus = argumentCollection[++i].ToLower().Equals("true") ? true : false;
							break;
					}
				}

				if (siteName == null || catalogNames == null || propertyName == null)
					return false;
			}
			catch (Exception e)
			{
				Console.WriteLine(Messages.ParseError + e.Message);
				throw e;
			}
			return true;
		}

		/// <summary>
		/// Method that displays the usage of the exe
		/// </summary>
		public static void Usage()
		{
			Console.WriteLine(Messages.Usage1);
			Console.WriteLine(Messages.Usage2);
			Console.WriteLine(Messages.Usage10);
			Console.WriteLine(Messages.Usage3);
			Console.WriteLine(Messages.Usage4);
			Console.WriteLine(Messages.Usage5);
			Console.WriteLine(Messages.Usage6);
			Console.WriteLine(Messages.Usage7);
			Console.WriteLine(Messages.Usage8);
			Console.WriteLine(Messages.Usage9);
		}

		static void Main(string[] args)
		{
			MarkEmptyCategories markEmptyCategories = null;
			CatalogSiteAgent siteAgent = null;
			CatalogContext catalogContext = null;
			ProductCatalog productCatalog = null;
			Category category = null;
			CatalogItemsDataSet emptyCategories = null;
			CatalogsDataSet catalogsDataSet = null;
			CategoryTools categoryTool = null;
			InventoryFilterOptions inventoryFilterOptions = null;
			StringCollection existingCatalogs = null;
			try
			{
				existingCatalogs = new StringCollection();
				markEmptyCategories = new MarkEmptyCategories();
				if (!markEmptyCategories.ParseArguments(args))
				{
					Console.WriteLine(Messages.RequiredItems);
					return;
				}

				//Create catalog context
				siteAgent = new CatalogSiteAgent();
				siteAgent.SiteName = markEmptyCategories.SiteName;
				siteAgent.IgnoreInventorySystem = true;
				siteAgent.AuthorizationMode = AuthorizationMode.NoAuthorization;
				catalogContext = CatalogContext.Create(siteAgent);

				#region Verify catalog names
				//Get all the catalog names 
				catalogsDataSet = catalogContext.GetCatalogs();
				foreach (DataRow row in catalogsDataSet.Catalogs)
				{
					existingCatalogs.Add(row[CatalogsDataSetSchema.CatalogName].ToString());
				}
				//Verify if the catalog names exist in the list
				foreach (string catalogName in markEmptyCategories.catalogNames)
				{
					if (!existingCatalogs.Contains(catalogName))
					{
						Console.WriteLine(catalogName + " " + Messages.InvalidCatalogName);
						return;
					}
				}
				#endregion

				#region Verify property exists
				try
				{
					catalogContext.GetProperty(markEmptyCategories.PropertyName);
				}
				catch (EntityDoesNotExistException ex)
				{
					Console.WriteLine(markEmptyCategories.PropertyName + " " + ex.Message);
					return;
				}
				#endregion
				//Creating the tool conttext
				categoryTool = new CategoryTools(markEmptyCategories.SiteName, markEmptyCategories.IgnoreInventorySystem);

				//Creating the inventory filter options
				inventoryFilterOptions = new InventoryFilterOptions();
				inventoryFilterOptions.FilterOutOfStockSkus = markEmptyCategories.FilterOutOfStockSkus;
				inventoryFilterOptions.FilterBackorderableSkus = markEmptyCategories.FilterBackorderableSkus;
				inventoryFilterOptions.FilterPreorderableSkus = markEmptyCategories.FilterPreorderableSkus;
				
				//Traversing each catalog in the list specified to find its empty categories and mark them.
				foreach (string catalogName in markEmptyCategories.catalogNames)
				{
					Console.WriteLine(Messages.CatalogBeingUpdated + " " + catalogName);
					productCatalog = catalogContext.GetCatalog(catalogName);
					emptyCategories = categoryTool.GetEmptyCategories(catalogName, inventoryFilterOptions);
					Console.WriteLine(Messages.CategoriesBeingUpdated);
					foreach (DataRow row in emptyCategories.CatalogItems.Rows)
					{
						category = (StaticCategory)productCatalog.GetCategory(row[CatalogItemsDataSetSchema.CategoryName].ToString());
						category[markEmptyCategories.PropertyName] = true;
						category.Save();
						Console.WriteLine(row[CatalogItemsDataSetSchema.CategoryName].ToString());
					}
				}
			}
			catch (Exception)
			{
				Console.WriteLine(Messages.GenericError);
				//Specify exception type!
				MarkEmptyCategories.Usage();
			}
		}
	}
}
